#ifndef _XX_YY_BMOCK_OUT_PTR_ARG_VAL
#define _XX_YY_BMOCK_OUT_PTR_ARG_VAL

#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/or.hpp>
#include <boost/static_assert.hpp>
#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>
#include "bmock_out_arg_val.hpp"
#include "bmock_raw_mem.hpp"
#include "detail/bmock_memory_managment.hpp"
#include "detail/bmock_in_mem_func_ptr.hpp"
#include <sstream>

namespace bmock {

	template<typename T>
	struct has_hex_format : public boost::mpl::or_<
		boost::is_same<T,int>
		,boost::is_same<T,unsigned int> 
		,boost::is_same<T,long>
		,boost::is_same<T,unsigned long> 
	>{};

	inline bool is_hex(const std::string &v) {
		const std::string prefix = v.substr(0,2);
		if("0x"==prefix) return true;
		if("0X"==prefix) return true;
		return false;
	}

	template<typename T>
	T __get_hex(const std::string &v) {
		std::istringstream    is(v.c_str());
		size_t p = (size_t)-1;
		is>>std::ws>>std::hex>>p>>std::dec;
		if(!is) throw message(INVALID_BINARY_INPUT,v);
		return (T)p;
	}

	template<typename T>
	struct is_char : public boost::is_same<
		char
		,typename boost::remove_const<T>::type
	> {};

	template<typename T>
	struct is_unsigned_char : public boost::is_same<
		unsigned char
		,typename boost::remove_const<T>::type
	> {};

	template<typename T>
	struct is_valid_out_ptr_type : public boost::mpl::or_<
		is_char<T>
		,boost::is_void<T>
		,is_unsigned_char<T>
	>{};

	template<typename T>
	inline typename boost::enable_if<boost::is_pointer<T>, T>::type 
		__get_out_value(const std::string &v);

	template<typename T>
	inline typename boost::enable_if<has_hex_format<T>,T>::type 
		__get_out_value(const std::string &v);

	template<typename T>
	inline typename boost::enable_if<boost::is_enum<T>,T>::type 
		__get_out_value(const std::string &v);

	template<typename T>
	inline typename boost::disable_if<boost::mpl::or_<boost::is_pointer<T>,boost::is_enum<T>,has_hex_format<T>,boost::is_same<T,bool>,boost::is_member_function_pointer<T> >,T>::type 
		__get_out_value(const std::string &v);

	template<typename T>
	inline typename boost::enable_if<boost::is_member_function_pointer<T>,T>::type 
		__get_out_value(const std::string &str);

	template<typename T>
	inline typename boost::enable_if<boost::is_same<T,bool>, T>::type 
		__get_out_value(const std::string &v);

	void insert_to_ptr_vector_(const boost::any& a);
	template<typename T>
	inline typename boost::disable_if_c<is_valid_out_ptr_type<T>::value||boost::is_function<T>::value,T *>::type 
		__get_out_ptr(const std::string &v) {
			T* x= new T(__get_out_value<T>(v));
			boost::shared_ptr<T>X(x);
			boost::any a(X);
			insert_to_ptr_vector_(a);
			return	x;
	}

	template<typename T>
	inline typename boost::disable_if<boost::mpl::or_<boost::is_pointer<T>,boost::is_enum<T>,has_hex_format<T>,boost::is_same<T,bool>,boost::is_member_function_pointer<T> >,T>::type 
		__get_out_value(const std::string &v);

	struct address_result
	{
		template<typename T>
		T get_result(const std::string &v)
		{
			return __get_hex<T>(v);
		}
	};

	const char *__save_out_str_ptr(const std::string &v);
	template<typename T>
	inline typename boost::enable_if_c<is_char<T>::value||is_unsigned_char<T>::value, T*>::type 
		__get_out_ptr(const std::string &v) {
			return (T *)__save_out_str_ptr(v);
	}	

	template<typename T>
	inline typename boost::enable_if<boost::is_function<T>,T*>::type 
		__get_out_ptr(const std::string &v) {
			return __get_hex<T *>(v);
	}

	template<typename T>
	inline typename boost::enable_if<boost::is_pointer<T>,T>::type 
		__get_out_value(const std::string &v) {
			return __get_out_ptr<typename boost::remove_pointer<T>::type>(v);
	}

	template<typename T>
	inline typename boost::enable_if<has_hex_format<T>,T>::type 
		__get_out_value(const std::string &v) {
			if(is_hex(v)) 
				return __get_hex<T>(v);
			else
				return boost::lexical_cast<T>(v);
	}

	template<typename T>
	inline typename boost::enable_if<boost::is_enum<T>,T>::type 
		__get_out_value(const std::string &v) {
			int x;
			x = boost::lexical_cast<int>(v);
			return T(x);
	}
	template<typename T>
	inline typename boost::disable_if<boost::mpl::or_<boost::is_pointer<T>,boost::is_enum<T>,has_hex_format<T>,boost::is_same<T,bool>,boost::is_member_function_pointer<T> >,T>::type 
		__get_out_value(const std::string &v) {
			return boost::lexical_cast<typename boost::remove_const<T>::type>(v);
	}
	template<typename T>
	inline typename boost::enable_if<boost::is_member_function_pointer<T>,T>::type 
		__get_out_value(const std::string &str) {
			return pointer_reinterpret<T>(__get_hex<void*>(str));
	}

	inline std::string __get_bool_value(const std::string &v)
	{
		if(v=="true")
			return "1";
		if(v=="false")
			return "0";
		return v;
	}
	template<typename T>
	inline typename boost::enable_if<boost::is_same<T,bool>, T>::type 
		__get_out_value(const std::string &v) {
			return boost::lexical_cast<T>(__get_bool_value(v));
	}
	
	template<typename T>
	inline typename boost::enable_if<boost::is_pointer<T>,bool>::type 
		__update_out_value(T *const val_ptr,const arg_checker &chk,const std::string &name,const std::string &v) {
			raw_mem(val_ptr,1).set_value(name,chk,v);
			return false;
	}
	
	template<typename T>
	inline typename boost::disable_if_c<boost::is_member_function_pointer<T>::value||boost::is_pointer<T>::value||
		boost::is_enum<T>::value||boost::is_same<T,char>::value||boost::is_same<T,unsigned char>::value,bool>::type 
		__update_out_value(T *const val_ptr,const arg_checker &chk,const std::string &name,const std::string &v) 
	{
		try {
			*val_ptr = boost::lexical_cast<T>(v);
		} catch (const boost::bad_lexical_cast &) {
			chk.update_failed(name);
			return true;
		}
		return false;
	}
	
	template<typename T>
	inline typename::boost::enable_if<boost::mpl::or_<boost::is_same<T,char>,boost::is_same<T,unsigned char> >, bool>::type
		__update_out_value(T *const val_ptr,const arg_checker &chk,const std::string &name,const std::string &v) {
			try {
				if ( v.empty() )
					*val_ptr = 0;
				else
					*val_ptr = boost::lexical_cast<T>(v.at(0));
			} catch (const boost::bad_lexical_cast &) {
				chk.update_failed(name);
				return true;
			}
			return false;
	}
		
	template<typename T>
	inline typename boost::enable_if<boost::is_enum<T>,bool>::type 
		__update_out_value(T *const val_ptr,const arg_checker &chk,const std::string &name,const std::string &v) {
			try {
				int x;
				x = boost::lexical_cast<int>(v);
				*val_ptr=T(x);
			} catch (const boost::bad_lexical_cast &) {
				chk.update_failed(name);
				return true;
			}
			return false;
	}
	template<typename T>
	inline typename boost::enable_if<boost::is_member_function_pointer<T>,bool>::type 
		__update_out_value(T* val_ptr,const arg_checker &chk,const std::string &name,const std::string &v) {
			try{
				*val_ptr=pointer_reinterpret<T>(__get_hex<void*>(v));
			}
			catch(...)
			{
				chk.update_failed(name);
				return true;
			}
			return false;

	}

	struct regular_result
	{
		template<typename R>
		static typename boost::enable_if<boost::is_void<typename boost::remove_pointer<R>::type>,R>::type
			get_return_ptr(const std::string &v)  {
				return __get_hex<R>(v);
		}

		template<typename R>
		static typename boost::disable_if<boost::is_void<typename boost::remove_pointer<R>::type>,R>::type
			get_return_ptr(const std::string &v) {
				return __get_out_value<R>(v);
		}

		template<typename T>
		static T get_result(const std::string &v)
		{
			return get_return_ptr<T>(v);
		}
	};

	template<typename T>
	struct out_ptr_arg_val : public out_arg_val {
		out_ptr_arg_val(const char *const n,T *const vp)
			:out_arg_val(n)
			,val_ptr_(vp)
		{}
	protected:
		~out_ptr_arg_val() {}
		bool null_supplied() const {
			return (NULL == val_ptr_);
		}
		bool update_out_value(const arg_checker &chk,const std::string &v) const {

			if(ignore(v)) return false;
			return __update_out_value(val_ptr_,chk,name_,v);
		}		
		std::string get_out_value() const {
			return __get_value(val_ptr_);
		}
		out_ptr_arg_val &operator =(const out_ptr_arg_val &) {
			return *this;
		}
		T *const val_ptr_;
	};

	template<typename T> 
	inline arg_val *const make_out_arg_val(const char *const n,T *vp) {
			return new out_ptr_arg_val<T>(n,vp);
	}
	template<typename T> 
	inline arg_val* const make_out_arg_val(const char *const n,T &vr) {
		return make_out_arg_val<T>(n,&vr);
	}	
}


#endif
